package com.pig4cloud.pig.ads.gdt.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.ads.gdt.service.GdtAccesstokenService;
import com.pig4cloud.pig.ads.gdt.service.GdtAdgroupDayReportService;
import com.pig4cloud.pig.ads.pig.mapper.PlatformJobInfoMapper;
import com.pig4cloud.pig.ads.pig.mapper.gdt.GdtAdgroupMapper;
import com.pig4cloud.pig.ads.service.AdvService;
import com.pig4cloud.pig.ads.utils.CronUtils;
import com.pig4cloud.pig.ads.utils.DateUtils;
import com.pig4cloud.pig.ads.utils.OEHttpUtils;
import com.pig4cloud.pig.api.dto.BudgetDto;
import com.pig4cloud.pig.api.entity.PlatformJobInfo;
import com.pig4cloud.pig.api.entity.ResponseBean;
import com.pig4cloud.pig.api.enums.StatusEnum;
import com.pig4cloud.pig.api.gdt.dto.GdtAdgroupDto;
import com.pig4cloud.pig.api.gdt.entity.GdtAdgroup;
import com.pig4cloud.pig.api.gdt.entity.GdtAdgroupDayReport;
import com.pig4cloud.pig.ads.clickhouse3399.mapper.GdtAdgroupDayReportMapper;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.api.util.Constants;
import com.pig4cloud.pig.api.util.MapUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;


/**
 * @author yk
 * @广告组 报表查询 服务实现类
 */

@Log4j2
@Service
public class GdtAdgroupDayReportServiceImpl extends ServiceImpl<GdtAdgroupDayReportMapper, GdtAdgroup> implements GdtAdgroupDayReportService {


	@Autowired
	private GdtAdgroupDayReportMapper gdtAdgroupDayReportMapper;

	@Autowired
	private AdvService advService;

	@Resource
	private GdtAdgroupMapper gdtAdgroupMapper;

	@Autowired
	private PlatformJobInfoMapper platformJobInfoMapper;

	@Resource
	private GdtAccesstokenService gdtAccesstokenService;

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	@Value(value = "${gdt_url}")
	private String gdtUrl;

	@Value(value = "${gdt_group_update_url}")
	private String gdtGroupUpdateUrl;

	/**
	 * 分页查询广告组报表数据
	 *
	 * @param req
	 * @return
	 */
	@Override
	public R getAdgroupDayReport(GdtAdgroupDto req) {
		//设置分页条件
		Page page = new Page();
		page.setSize(req.getSize());
		page.setCurrent(req.getCurrent());
		//获取当前账号下 广点通广告账户列表
		Integer id = SecurityUtils.getUser().getId();
		List<String> accountList = advService.getOwnerAdv(SecurityUtils.getUser().getId(), PlatformTypeEnum.GDT.getValue());
		if (null == accountList || accountList.size() <= 0) {
			return R.ok(page);
		}
		//设置参数:广告账户ids集合
		//前端没传广告账户参数，通过当前账户查询
		String[] accountIds = req.getAccountIds();
		if (null == accountIds || accountIds.length <= 0) {
			req.setAccountList(accountList);
		} else {//前端有传广告账户参数
			List<String> accountIdsList = Arrays.asList(accountIds);
			req.setAccountList(accountIdsList);
		}

		String sdate = req.getSdate();
		String edate = req.getEdate();
		//当没有传递时间的时候 默认统计当天的
		if (StringUtils.isBlank(sdate) || StringUtils.isBlank(edate)) {
			//数据能查询到的最早日期
			//	String sDate = "2016-10-26";
			req.setSdate(DateUtils.dateToString(new Date(), "yyyy-MM-dd"));
			req.setEdate(DateUtils.dateToString(new Date(), "yyyy-MM-dd"));
		}
		IPage<GdtAdgroupDayReport> gdtAdgroupDailyReportList = null;
		gdtAdgroupDailyReportList = gdtAdgroupDayReportMapper.selectGdtAdgroupReport(req);
		// 根据拉取的数值 进行点击计算
		List<GdtAdgroupDayReport> list = gdtAdgroupDailyReportList.getRecords();
		if (CollectionUtils.isNotEmpty(list)) {
			for (int i = 0; i < list.size(); i++) {
				GdtAdgroupDayReport adgroupDailyReport = list.get(i);
				/*//点击量
				BigDecimal vcc = new BigDecimal(adgroupDailyReport.getValidClickCount());
				//展现量
				BigDecimal vc = new BigDecimal(adgroupDailyReport.getViewCount());
				//消耗
				BigDecimal cost = new BigDecimal(adgroupDailyReport.getCost());
				if (vcc.compareTo(BigDecimal.ZERO) == 1 && vc.compareTo(BigDecimal.ZERO) == 1) {
					//点击率
					adgroupDailyReport.setCtr((vcc.divide(vc, 2, BigDecimal.ROUND_HALF_UP)).toString());
				}
				if (vcc.compareTo(BigDecimal.ZERO) == 1 && cost.compareTo(BigDecimal.ZERO) == 1) {
					//点击均价
					adgroupDailyReport.setCpc((cost.divide(vcc, 2, BigDecimal.ROUND_HALF_UP)).toString());
				}*/
				Long adgroupId = adgroupDailyReport.getAdgroupId();
				GdtAdgroup gdtAdGroup = gdtAdgroupMapper.selectOne(Wrappers.<GdtAdgroup>query().lambda()
						.eq(GdtAdgroup::getAdgroupId, adgroupId));
				//如果mysql有记录。理论上mysql和CH数据是一致的，否则就是同步有问题！
				if (Objects.nonNull(gdtAdGroup)) {
					//将mysql的开关状态设置到列表记录中
					BigDecimal dailyBudget = gdtAdGroup.getDailyBudget().divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
					BigDecimal bidAmount = gdtAdGroup.getBidAmount().divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
					adgroupDailyReport.setConfiguredStatus(gdtAdGroup.getConfiguredStatus());
					adgroupDailyReport.setAdgroupName(gdtAdGroup.getAdgroupName());
					adgroupDailyReport.setSystemStatus(gdtAdGroup.getSystemStatus());
					adgroupDailyReport.setStatus(gdtAdGroup.getStatus());
					adgroupDailyReport.setBidMode(gdtAdGroup.getBidMode());
					adgroupDailyReport.setBidAmount(bidAmount);
					adgroupDailyReport.setDailyBudget(dailyBudget);
				}

				//判断是否超出预警
				String costWarning = stringRedisTemplate.opsForValue().get(Constants.GDT_REDIS_ADGROUP_COST_WARNING_KEY_PRIX_ + adgroupId);
				adgroupDailyReport.setCostWarning(StringUtils.isBlank(costWarning) ? "N" : costWarning);
			}
		}
		gdtAdgroupDailyReportList.setRecords(list);
		return R.ok(gdtAdgroupDailyReportList, "查询成功");
	}

	@Override
	public R updateOnOff(GdtAdgroupDto req) {
		String configuredStatus = req.getConfiguredStatus();
		if (null == configuredStatus) {
			return R.failed("广告组开关不允许为空");
		}
		if (!(configuredStatus.equals("AD_STATUS_NORMAL") || configuredStatus.equals("AD_STATUS_SUSPEND"))) {
			return R.failed("广告组开关输入不合法：请输入\"AD_STATUS_NORMAL\"或者\"AD_STATUS_SUSPEND\"");
		}
		//获取主键
		Long adgroupId = req.getAdgroupId();
		String adgIdStr = String.valueOf(adgroupId);
		if (null == adgroupId) {
			return R.failed("广告组id不允许为空");
		}
		GdtAdgroup adg = gdtAdgroupMapper.selectById(adgroupId);
		if (Objects.isNull(adg)) {
			return R.failed("无此广告组");
		}
		String accountId = adg.getAccountId();
		Map<String, String> map = gdtAccesstokenService.fetchAccesstoken(accountId);
		if (null == map || StringUtils.isEmpty(map.get("access_token"))) {
			return R.failed("广点通账户授权失败，请重试！");
		}
		String urlstr = gdtUrl + gdtGroupUpdateUrl + "?" + MapUtils.queryString(map);
		log.info("修改广点通广告组开关： request:" + urlstr);
		Map<String, Object> params = new HashMap<>();
		params.put("account_id", accountId);
		params.put("adgroup_id", adgroupId);
		params.put("configured_status", configuredStatus);
		//提交请求广点通修改广告组接口
		String resultStr = OEHttpUtils.doPost(urlstr, params);
		log.info("修改广点通广告组开关： response:" + resultStr);
		ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
		String adgId = null;
		if (resBean != null && "0".equals(resBean.getCode())) {
			//同步修改数据库
			JSONObject jsonObj = resBean.getData();
			adgId = jsonObj.getString("adgroup_id");
		}
		if (StringUtils.isBlank(adgId)) {
			return R.failed("获取广告组id失败");
		}

		if (!adgIdStr.equals(adgId)) {
			return R.failed("投放平台与广点通广告组id不匹配");
		}
		int i = gdtAdgroupMapper.update(req);
		return R.ok(i, "广告组开关修改成功");
	}

	@Override
	public R updateBidAmount(GdtAdgroupDto req) {
		BigDecimal bidAmount = req.getBidAmount();
		if (null == bidAmount || bidAmount.compareTo(BigDecimal.ZERO) == -1) {
			return R.failed("未提供广告组出价，或出价小于0");
		}
		Long adgroupId = req.getAdgroupId();
		String adgIdStr = String.valueOf(adgroupId);
		if (null == adgroupId) {
			return R.failed("广告组id不允许为空");
		}
		GdtAdgroup adg = gdtAdgroupMapper.selectById(adgroupId);
		if (Objects.isNull(adg)) {
			return R.failed("无此广告组");
		}


		String bidMode = adg.getBidMode();

		if (StringUtils.isBlank(bidMode)) {
			String billEvent = adg.getBillingEvent();
			if (StringUtils.isBlank(billEvent)) {
				return R.failed("计费类型与出价方式不能同时为空");
			} else {
				String bidStrategy = adg.getBidStrategy();
				switch (billEvent) {
					case "BILLINGEVENT_CLICK":
						if ("BID_STRATEGY_UNSUPPORTED".equals(bidStrategy)) {
							bidMode = "BID_MODE_CPC";
						} else {
							bidMode = "BID_MODE_OCPC";
						}
						break;
					case "BILLINGEVENT_IMPRESSION":
						if ("BID_STRATEGY_UNSUPPORTED".equals(bidStrategy)) {
							bidMode = "BID_MODE_CPM";
						} else {
							bidMode = "BID_MODE_OCPM";
						}
						break;
					case "BILLINGEVENT_APP_DOWNLOAD":
						bidMode = "BID_MODE_CPA";
						break;
					default:
						return R.failed("没有匹配到计费类型");
				}
			}
		}


		Boolean flag = Boolean.TRUE;
		if (("BID_MODE_CPC").equals(bidMode)) {
			flag = bidAmount.compareTo(new BigDecimal("0.1")) == -1 || bidAmount.compareTo(new BigDecimal("100")) == 1;
			if (flag) {
				return R.failed("出价方式为CPC时，取值范围：0.1-100元");
			}
		}
		if (("BID_MODE_CPA").equals(bidMode)) {
			flag = bidAmount.compareTo(new BigDecimal("1")) == -1 || bidAmount.compareTo(new BigDecimal("500")) == 1;
			if (flag) {
				return R.failed("出价方式为CPA时，取值范围：1-500元");
			}
		}
		if (("BID_MODE_CPM").equals(bidMode)) {
			flag = bidAmount.compareTo(new BigDecimal("1.5")) == -1 || bidAmount.compareTo(new BigDecimal("999")) == 1;
			if (flag) {
				return R.failed("出价方式为CPM时，取值范围：1.5-999元");
			}
		}
		if (("BID_MODE_OCPC").equals(bidMode) || ("BID_MODE_OCPM").equals(bidMode)) {
			flag = bidAmount.compareTo(new BigDecimal("0.1")) == -1 || bidAmount.compareTo(new BigDecimal("5000")) == 1;
			if (flag) {
				return R.failed("出价方式为oCPC或OCPM时，取值范围：0.1-5000元");
			}
		}
		String accountId = adg.getAccountId();
		Map<String, String> map = gdtAccesstokenService.fetchAccesstoken(accountId);
		if (null == map || StringUtils.isEmpty(map.get("access_token"))) {
			return R.failed("广点通账户授权失败，请重试！");
		}
		String urlstr = gdtUrl + gdtGroupUpdateUrl + "?" + MapUtils.queryString(map);
		log.info("修改广点通广告组出价： request:" + urlstr);
		int bid = bidAmount.multiply(new BigDecimal("100")).intValue();
		Map<String, Object> params = new HashMap<>();
		params.put("account_id", accountId);
		params.put("adgroup_id", adgroupId);
		params.put("bid_amount", bid);
		//提交请求广点通修改广告组接口
		String resultStr = OEHttpUtils.doPost(urlstr, params);
		log.info("修改广点通广告组出价： response:" + resultStr);
		JSONObject obj = JSON.parseObject(resultStr);
		String code = obj.getString("code");
		if (!StringUtils.equals(code, "0")) {
			String message = obj.getString("message_cn");
			return R.failed("更新出价异常:" + message);
			//return R.failed("update bidAmount exception:" + message.replaceAll("(\\ )+\\w+", "") + "（单位：分）");
		}
		ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
		String adgId = null;
		if (resBean != null && "0".equals(resBean.getCode())) {
			//同步修改数据库
			JSONObject jsonObj = resBean.getData();
			adgId = jsonObj.getString("adgroup_id");
		}
		if (StringUtils.isBlank(adgId)) {
			return R.failed("获取广告组id失败");
		}

		if (!adgIdStr.equals(adgId)) {
			return R.failed("投放平台与广点通广告组id不匹配");
		}
		req.setBidAmount(new BigDecimal(bid));
		int i = gdtAdgroupMapper.update(req);
		return R.ok(i, "广告组出价修改成功");
	}

	@Override
	public R updateDailyBudget(GdtAdgroupDto req) {
		BigDecimal dailyBudget = req.getDailyBudget();
		if (null == dailyBudget || dailyBudget.compareTo(BigDecimal.ZERO) == -1) {
			return R.failed("未提供预算，或预算小于0");
		}
		if (dailyBudget.compareTo(BigDecimal.ZERO) == 1) {
			if (dailyBudget.compareTo(new BigDecimal("50")) == -1 || dailyBudget.compareTo(new BigDecimal("4000000")) == 1) {
				return R.failed("输入范围50-40,000,000元/天，修改幅度≥ 50元，下调日预算时：新预算≥当前广告日消耗+50元，对于微信朋友圈广告,且新预算≥当前广告日消耗*1.5倍");
			}
		}
		Long adgroupId = req.getAdgroupId();
		String adgIdStr = String.valueOf(adgroupId);
		if (null == adgroupId) {
			return R.failed("广告组id不允许为空");
		}
		GdtAdgroup adg = gdtAdgroupMapper.selectById(adgroupId);
		if (Objects.isNull(adg)) {
			return R.failed("无此广告组");
		}
		String accountId = adg.getAccountId();
		Map<String, String> map = gdtAccesstokenService.fetchAccesstoken(accountId);
		if (map == null || StringUtils.isEmpty(map.get("access_token"))) {
			return R.failed("广点通账户授权失败，请重试！");
		}
		String urlstr = gdtUrl + gdtGroupUpdateUrl + "?" + MapUtils.queryString(map);
		log.info("修改广点通广告组日预算： request:" + urlstr);
		int budget = dailyBudget.multiply(new BigDecimal("100")).intValue();
		Map<String, Object> params = new HashMap<>();
		params.put("account_id", accountId);
		params.put("adgroup_id", adgroupId);
		params.put("daily_budget", budget);
		//提交请求广点通修改广告组接口
		String resultStr = OEHttpUtils.doPost(urlstr, params);
		//结果为null，当做失败
		if (StringUtils.isBlank(resultStr)) {
			return R.failed("第三方更新失败:resultStr is null");
		}
		JSONObject obj = JSON.parseObject(resultStr);
		String code = obj.getString("code");
		if (!StringUtils.equals(code, "0")) {
			//String message = obj.getString("message_cn");
			//return R.failed("update bidAmount exception:" + message.replaceAll("(\\ )+\\w+", ""));
			return R.failed("输入范围50-40,000,000元/天，修改幅度≥ 50元，下调日预算时：新预算≥当前广告日消耗+50元，对于微信朋友圈广告,且新预算≥当前广告日消耗*1.5倍");
		}
		log.info("修改广点通广告组出价： response:" + resultStr);
		ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
		String adgId = null;
		if (resBean != null && "0".equals(resBean.getCode())) {
			//同步修改数据库
			JSONObject jsonObj = resBean.getData();
			adgId = jsonObj.getString("adgroup_id");
		}
		if (StringUtils.isBlank(adgId)) {
			return R.failed("获取广告组id失败");
		}
		if (!adgIdStr.equals(adgId)) {
			return R.failed("投放平台与广点通广告组id不匹配");
		}

		req.setDailyBudget(new BigDecimal(String.valueOf(budget)));
		//req.setDailyBudget(new BigDecimal("0"));
		int i = gdtAdgroupMapper.update(req);
		adg.setBidAmount(adg.getBidAmount().divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP));
		adg.setDailyBudget(new BigDecimal(String.valueOf(dailyBudget)));
		return R.ok(adg, "广告组日预算修改成功");
	}


	@Override
	public R orderDailyBudget(BudgetDto budgetDto, Integer operate) {
		BigDecimal budget = budgetDto.getBudget();
		if (null == budget || budget.compareTo(BigDecimal.ZERO) == -1) {
			return R.failed("未提供预算，或预算小于0");
		}
		if (budget.compareTo(BigDecimal.ZERO) == 1) {
			if (budget.compareTo(new BigDecimal("50")) == -1 || budget.compareTo(new BigDecimal("4000000")) == 1) {
				return R.failed("输入范围50-40,000,000元/天，修改幅度≥ 50元，下调日预算时：新预算≥当前广告日消耗+50元，对于微信朋友圈广告,且新预算≥当前广告日消耗*1.5倍");
			}
		}
		Long unionId = null;
		String jobName = null;
		//广告组 ==4
		unionId = budgetDto.getAdgroupId();
		String adgIdStr = String.valueOf(unionId);
		if (null == unionId) {
			return R.failed("广告组id不允许为空");
		}
		GdtAdgroup adg = gdtAdgroupMapper.selectById(unionId);
		if (Objects.isNull(adg)) {
			return R.failed("无此广告组");
		}

		jobName = "修改广点通广告组预算";
		budgetDto.setAdvertiserId(adg.getAccountId());
		//添加日限额方式
		if (budget.compareTo(BigDecimal.ZERO) == 0) {
			budgetDto.setBudgetMode("BUDGET_MODE_INFINITE");
		} else {
			budgetDto.setBudgetMode("BUDGET_MODE_DAY");
		}
		budgetDto.setType(StatusEnum.TYPE_TWO.getStatus());
		//远程调用定时器项目创建定时任务
		try {
			//第二天凌晨执行
			Date exeTime = DateUtils.getFirstSecondOfOneDay(DateUtils.addDays(new Date(), 1));
			String cronDate = CronUtils.getCron(exeTime);
			String exeParam = JSONObject.toJSONString(budgetDto);
			List<PlatformJobInfo> platformJobInfoList = platformJobInfoMapper.selectList(Wrappers.<PlatformJobInfo>query().lambda().eq(PlatformJobInfo::getUniqueId,unionId).eq(PlatformJobInfo::getOperateType,operate).eq(PlatformJobInfo::getType,StatusEnum.AD_TYPE_TWO.getStatus()).gt(PlatformJobInfo::getExecuteTime, new Date()));
			if (CollectionUtils.isEmpty(platformJobInfoList)) {
				//新增  -- 已经执行的以新增为标准，未执行以修改为准
				platformJobInfoMapper.insert(new PlatformJobInfo(unionId, jobName, cronDate, exeTime, exeParam, StatusEnum.JOB_STATUS_ONE.getStatus(), operate, StatusEnum.AD_TYPE_TWO.getStatus()));
			} else {
				//修改账号修改任务-----如果还没有执行
				platformJobInfoMapper.updateById(new PlatformJobInfo(unionId, exeParam, cronDate, exeTime, StatusEnum.JOB_STATUS_ONE.getStatus(), platformJobInfoList.get(0).getId()));
			}

		} catch (Exception e) {
			log.error("budgetUpdate  cron  is error:", e);
			return R.failed("定时修改日预算异常，请稍后重试");
		}
		return R.ok(budget,"定时修改日预算成功");
	}

	/**
	 *查询广告组预约当天定时设置预算值
	 * @param
	 * @return
	 */
	@Override
	public R getAdvertiserJobBudget(BudgetDto budgetDto, Integer operateType) {
		Long unionId = null;
		unionId = budgetDto.getAdgroupId();
		if (null == unionId) {
			return R.failed("广告组id不允许为空");
		}
		GdtAdgroup adg = gdtAdgroupMapper.selectById(unionId);
		if (Objects.isNull(adg)) {
			return R.failed("无此广告组");
		}
		BigDecimal budget = null;
		List<PlatformJobInfo> platformJobInfoList = platformJobInfoMapper.selectList(Wrappers.<PlatformJobInfo>query().lambda().eq(PlatformJobInfo::getUniqueId,unionId).eq(PlatformJobInfo::getOperateType,operateType).eq(PlatformJobInfo::getType,StatusEnum.AD_TYPE_TWO.getStatus()).gt(PlatformJobInfo::getExecuteTime, new Date()));
		if (CollectionUtils.isNotEmpty(platformJobInfoList)) {
			String exParam = platformJobInfoList.get(0).getExecutorParam();
			BudgetDto dto = null;
			if (StringUtils.isNotBlank(exParam)) {
				dto = JSONObject.parseObject(exParam, BudgetDto.class);
				budget = dto.getBudget();
			}
		}
		return R.ok(budget,"获取次日日预算成功");
	}

}

